﻿using System;
using System.Windows.Media;

namespace Redbrick.Silverlight.Common
{
	public sealed class HSLColor
	{

		#region Constructors

		public HSLColor() { }
		public HSLColor(double alpha, double hue, double saturation, double lightness)
		{
			this.A = alpha;
			this.H = hue;
			this.S = saturation;
			this.L = lightness;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Get/Set Alpha value,valid values from 0 to 1
		/// </summary>
		public double A { get; set; }
		/// <summary>
		/// Get/Set Hue value,valid values from 0 to 360
		/// </summary>
		public double H { get; set; }
		/// <summary>
		/// Get/Set Saturation value,valid values from 0 to 1
		/// </summary>
		public double S { get; set; }
		/// <summary>
		/// Get/Set Luminosity value,valid values from 0 to 1
		/// </summary>
		public double L { get; set; }

		#endregion

		#region Methods

		private static double ByteToPercent(byte v)
		{
			double d = v;
			d /= 255;
			return d;
		}
		private static byte PercentToByte(double pct)
		{
			pct *= 255;
			pct += .5;
			if (pct > 255) pct = 255;
			if (pct < 0) pct = 0;
			return (byte)pct;
		}

		public static implicit operator HSLColor(Color c)
		{
			return HSLColor.FromArgb(c.A, c.R, c.G, c.B);
		}
		public static implicit operator Color(HSLColor hsl)
		{
			double q = 0;
			if (hsl.L < .5)
			{
				q = hsl.L * (1 + hsl.S);
			}
			else
			{
				q = hsl.L + hsl.S - (hsl.L * hsl.S);
			}
			double p = (2 * hsl.L) - q;
			double hk = hsl.H / 360;
			double r = hsl.GetComponent(hsl.Normalize(hk + (1.0 / 3.0)), p, q);
			double g = hsl.GetComponent(hsl.Normalize(hk), p, q);
			double b = hsl.GetComponent(hsl.Normalize(hk - (1.0 / 3.0)), p, q);
			return Color.FromArgb(HSLColor.PercentToByte(hsl.A), HSLColor.PercentToByte(r), HSLColor.PercentToByte(g), HSLColor.PercentToByte(b));
		}

		public static HSLColor FromArgb(byte A, byte R, byte G, byte B)
		{
			HSLColor c = FromRgb(R, G, B);
			c.A = HSLColor.ByteToPercent(A);
			return c;
		}

		public static HSLColor FromRgb(byte R, byte G, byte B)
		{
			HSLColor c = new HSLColor();
			c.A = 1;
			double r = HSLColor.ByteToPercent(R);
			double g = HSLColor.ByteToPercent(G);
			double b = HSLColor.ByteToPercent(B);
			double max = Math.Max(b, Math.Max(r, g));
			double min = Math.Min(b, Math.Min(r, g));
			if (max == min)
			{
				c.H = 0;
			}
			else if (max == r && g >= b)
			{
				c.H = 60 * ((g - b) / (max - min));
			}
			else if (max == r && g < b)
			{
				c.H = 60 * ((g - b) / (max - min)) + 360;
			}
			else if (max == g)
			{
				c.H = 60 * ((b - r) / (max - min)) + 120;
			}
			else if (max == b)
			{
				c.H = 60 * ((r - g) / (max - min)) + 240;
			}

			c.L = .5 * (max + min);
			if (max == min)
			{
				c.S = 0;
			}
			else if (c.L <= .5)
			{
				c.S = (max - min) / (2 * c.L);
			}
			else if (c.L > .5)
			{
				c.S = (max - min) / (2 - 2 * c.L);
			}
			return c;
		}

		public HSLColor Lighten(double percent)
		{
			HSLColor c = new HSLColor();
			c.A = this.A;
			c.H = this.H;
			c.S = this.S;
			c.L = Math.Min(Math.Max(this.L + percent, 0), 1);
			return c;
		}

		public HSLColor Darken(double percent)
		{
			return Lighten(-percent);
		}

		private double Normalize(double d)
		{
			if (d < 0) d += 1;
			if (d > 1) d -= 1;
			return d;
		}


		private double GetComponent(double tc, double p, double q)
		{
			if (tc < (1.0 / 6.0))
			{
				return p + ((q - p) * 6 * tc);
			}
			if (tc < .5)
			{
				return q;
			}
			if (tc < (2.0 / 3.0))
			{
				return p + ((q - p) * 6 * ((2.0 / 3.0) - tc));
			}
			return p;
		}

		public override string ToString()
		{
			return ((Color)this).ToString();
		}

		#endregion

	}
}
